/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ASintactico;

/**
 *
 * @author Alfredo
 */
import Alexico.*;
import ASemantico.atributo;
import ASemantico.controlesSemanticos;
import GeneradorCodigo.generador;
import java.util.ArrayList;

public class ASintactico {

    token preanalisis;
    analizadorLex lex;
    first First;
    controlesSemanticos cs;
    generador gc;
    boolean esContructor = false;
    String posibleVariable = "";
    ArrayList<String> variblesPorAlmacenar = new ArrayList<String >();
    boolean asigMultiple = false;
   
    public ASintactico(String InFile, String OutFile) throws Exception {
        lex = new analizadorLex(InFile);
    }

    public ASintactico(String InFile) throws Exception {
        try {
            lex = new analizadorLex(InFile);
            cs = new controlesSemanticos();
            //enlazo la tabla de simbolos con el generador de codigos;                        
            gc = new generador(cs.getTabladeSimbolos(), "C:\\generacioncodigo\\ceiasm.txt");
            //gc = new generador(cs.getTabladeSimbolos(), InFile+".ceiasm");
            gc.inicializarInstrucciones();
            preanalisis = this.LeerToken();
            if (preanalisis == null) {
                //TODO veificar i preguntar por nombre y ver si es $
                throw new customException(customException.ArchivoFuenteVacioCODE);
            } else {
                this.First = new first();
                s();
                System.out.println("\n-------------------------------------------------------------\n");
                System.out.println("El programa es sintacticamente correcto!!!\n");
                System.out.println("-------------------------------------------------------------");

            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private token LeerToken() throws Exception {
        String msg = "";
        token t = lex.getToken();
        if (t != null) {
            if (cs != null && t != null) {
                cs.setLineaActual(t.getNumLinea());
            }
            return t;
        } else {
            //TODO
            throw new customException("Fin de archivo inesperado. ");
        }
    }

    private void parea(token tEsperado) throws Exception {
        if (preanalisis.getNombre().equals(tEsperado.getNombre())) {
            preanalisis = LeerToken();
        } else {
            throw new customException(tEsperado, preanalisis);
        }
    }

    private void s() throws Exception {
        //<S>::=<START> $ 
        if (this.preanalisis.getNombre().equals("PESOS")) {
            throw new customException(customException.ArchivoFuenteVacioCODE);
        } else {
            cs.inicializacionControlesSemanticos();
            start();
            //Pendiendete estaMain()
            cs.estaMain();
            token t = new token("$");
            t.setNombre("PESOS");
            if (this.preanalisis.getNombre().equals("PESOS")) {
                parea(t);
                cs.comprobarEspecificacionesPendientes();
                gc.mostrarInstruccionesGeneradas();
                gc.generarCompilado();

            } else {
                throw new customException(customException.ArchivoFuenteMalFormadoCODE, this.preanalisis);
            }
        }
    }

    private void start() throws Exception {
        // <Start>::= <classDef> <ClassDefList><classExp> <ClassExpList> 
        gc.data();
        classDef();
        //cs.comprobadorSemantico();
        classDefList();
        //cs.comprobadorSemantico();
        cs.comprobarDeclaracionesPendientes();
        gc.code();
        classExp();
        classExpList();
        cs.comprobarEspecificacionesPendientes();
        //imprime lo que tiene la tabla de simbolos
        cs.comprobadorSemantico();
    }

    private void classDefList() throws Exception {
        //<ClassDefList>::=<ClassDef><ClassDefList>|€
        if (First.pertenece(this.preanalisis.getNombre(), "ClassDef")) {
            classDef();
            classDefList();
        }
        // Sino lambda
    }

    private void classExpList() throws Exception {
        //<ClassList>::=<ClassExp><ClassExpList>  | €
        if (First.pertenece(this.preanalisis.getNombre(), "ClassExp")) {
            classExp();
            classExpList();
        }
        //Sino lambda
    }

    private atributo hierarchy() throws Exception {
        //<hierarchy>::=  EXTENDS  identifier | €
        token t = new token("extends");
        t.setNombre("EXTENDS");
        atributo sHierachy;
        if (this.preanalisis.getNombre().equals("EXTENDS")) {
            parea(t);
            t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                sHierachy = cs.crearSintetizadoHierarchy(this.preanalisis.getLexema());
                parea(t);
            } else {
                throw new customException(customException.ExtendsSinTipoCODE, t, this.preanalisis);
            }
        } else {
            //lambda
            sHierachy = cs.crearSintetizadoHierarchy(cs.getObjectType());
        }
        return sHierachy;
    }

    private void classDef() throws Exception {

        //<ClassDef>::= classDef  identifier <Hierarchy> { <ClassDefBody> }
        token t = new token("classDef");
        t.setNombre("CLASSDEF");
        atributo sClassDef;
        if (this.preanalisis.getNombre().equals("CLASSDEF")) {
            parea(t);
            t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                cs.agregarClaseDef(this.preanalisis.getLexema());
                cs.setClaseActual(this.preanalisis.getLexema());
                parea(t);
                sClassDef = hierarchy();
                t = new token("{");
                t.setNombre("LLAVE_A");
                if (this.preanalisis.getNombre().equals("LLAVE_A")) {
                    cs.agregarHerencia(cs.claseActual(), sClassDef.getNombreClase());
                    parea(t);
                    classDefBody();
                    t = new token("}");
                    t.setNombre("LLAVE_C");
                    if (this.preanalisis.getNombre().equals("LLAVE_C")) {
                        //Pendiendete comprobarTieneConstructor();
                        cs.comprobarTieneConstructor();
                        gc.crearVT(cs.claseActual());
                        parea(t);
                    } else {
                        //errorsintactico LLAVE_C
                        throw new customException(customException.ClassDefSinLLaveCierraCODE, t, preanalisis);
                    }
                } else {
                    //errorsintactico llave_A
                    throw new customException(customException.ClassDefSinLLaveAbreCODE, t, preanalisis);
                }
            } else {
                //errorsintactico ID
                throw new customException(customException.ClassDefIdentificadorCODE, t, preanalisis);
            }
        } else {
            //errorsintactico classdef
            //este error no puede darse dado que antes de entrar al metodo classDef se pregunta si el token de preanalisis pertenecea First de classDef
            throw new customException(t, preanalisis);
        }
    }

    private void classDefBody() throws Exception {
        //<ClassDefBody>::= id <ConstMethodDef> |
        //                  <PrimitiveType>   <MethodDef> |
        //                  void <MethodDef> |
        //                  €

        token t = new token("identificador");
        t.setNombre("ID");
        atributo sClassDefBody;
        if (this.preanalisis.getNombre().equals("ID")) {
            sClassDefBody = cs.crearSintetizadoConsMethodDef(this.preanalisis.getLexema());
            parea(t);
            constMethodDef(sClassDefBody);
        } else {
            t = new token("void");
            t.setNombre("VOID");
            if (this.preanalisis.getNombre().equals("VOID")) {
                parea(t);
                sClassDefBody = cs.crearHeredadoMethodDef(cs.getVoidType());
                methodDef(sClassDefBody);
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "PrimitiveType")) {
                    sClassDefBody = primitiveType();
                    cs.crearHeredadoMethodDef(sClassDefBody.getType());
                    methodDef(sClassDefBody);
                }
                //Else LAMBDA
            }
        }
    }

    private void constMethodDef(atributo heredado) throws Exception {
        //<ConstMethodDef> ::= <FormalArgs> ; <ClassDefBody> |
        //      		id <FormalArgs> ; <ClassDefBodyMethod>

        token t = new token("identificador");
        t.setNombre("ID");
        if (this.preanalisis.getNombre().equals("ID")) {
            String nombreMetodo = this.preanalisis.getLexema();
            parea(t);
            cs.limpiarListaTemporalParametros();
            formalArgs();
            t = new token(";");
            t.setNombre("SEP");
            if (this.preanalisis.getNombre().equals("SEP")) {
                cs.agregarClasePendiendteTS(heredado.getIdentificadorOTipo());
                cs.agregarMetodo(heredado.getIdentificadorOTipo(), nombreMetodo);
                parea(t);
                classDefBodyMethod();
            } else {
                throw new customException(customException.ConstMethodSeparadorCODE, this.preanalisis);
            }
        } else {
            if (First.pertenece(this.preanalisis.getNombre(), "FormalArgs")) {
                cs.limpiarListaTemporalParametros();
                formalArgs();
                t = new token(";");
                t.setNombre("SEP");
                if (this.preanalisis.getNombre().equals("SEP")) {
                    cs.controlarNombreConstructor(heredado.getIdentificadorOTipo());
                    cs.agregarConstructor();
                    parea(t);
                    classDefBody();
                } else {
                    throw new customException(customException.ConstMethodSeparadorCODE, this.preanalisis);
                }

            } else {
                //TODO error en la construccion de un metodo o constructor
                throw new customException(customException.ConstMethodCODE, preanalisis);
            }
        }
    }

    private void methodDef(atributo heredado) throws Exception {
        // <MethodDef> ::= id <FormalArgs> ; <ClassDefBodyMethod>
        token t = new token("identificador");
        t.setNombre("ID");
        if (this.preanalisis.getNombre().equals("ID")) {
            String identificador = this.preanalisis.getLexema();
            parea(t);
            cs.limpiarListaTemporalParametros();
            formalArgs();
            t = new token(";");
            t.setNombre("SEP");
            if (this.preanalisis.getNombre().equals("SEP")) {
                cs.agregarMetodo(heredado.getType(), identificador);
                parea(t);
                classDefBodyMethod();
            } else {
                throw new customException(customException.ConstMethodSeparadorCODE, this.preanalisis);
            }
        } else {
            throw new customException(customException.MethodDefIdentificadorCODE, t, preanalisis);
        }
    }

    private void classDefBodyMethod() throws Exception {
        //<ClassDefBodyMethod> ::= void <MethodDef> |
        //			<PrimitiveType> <MethodDef> |
        //                      id <MethodDef>|
        //                      €
        token t = new token("identificador");
        t.setNombre("ID");
        atributo sClassDefBodyMethod;
        if (this.preanalisis.getNombre().equals("ID")) {
            cs.agregarClasePendiendteTS(this.preanalisis.getLexema());
            sClassDefBodyMethod = cs.crearHeredadoMethodDef(this.preanalisis.getLexema());
            parea(t);
            methodDef(sClassDefBodyMethod);
        } else {
            t = new token("void");
            t.setNombre("VOID");
            if (this.preanalisis.getNombre().equals("VOID")) {
                parea(t);
                sClassDefBodyMethod = cs.crearHeredadoMethodDef(cs.getVoidType());
                methodDef(sClassDefBodyMethod);
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "PrimitiveType")) {
                    sClassDefBodyMethod = primitiveType();
                    methodDef(sClassDefBodyMethod);
                }
            }
        }
    }
    //Pendiente Sin mofificar nada que tenga que ver con classEXP

    private void classExp() throws Exception {
        //<ClassExp>::= class identifier <hierarchy>  { <ClassBody >}
        token t = new token("class");
        t.setNombre("CLASS");
        atributo sClassExp;
        if (this.preanalisis.getNombre().equals("CLASS")) {
            parea(t);
            t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                cs.setClaseActual(this.preanalisis.getLexema());
                parea(t);
                sClassExp = hierarchy();
                cs.controlarDefinicionDeClase(sClassExp.getNombreClase());
                t = new token("{");
                t.setNombre("LLAVE_A");
                if (this.preanalisis.getNombre().equals("LLAVE_A")) {
                    parea(t);
                    //gc.iniciarClase(cs.claseActual());
                    classBody();
                    t = new token("}");
                    t.setNombre("LLAVE_C");
                    if (this.preanalisis.getNombre().equals("LLAVE_C")) {
                        cs.quitarClaseDeListaPendienteEspecificacion();
                        parea(t);
                    } else {
                        //errorsintactico LLAVE_C
                        throw new customException(customException.ClassSinLLaveCierraCODE, t, preanalisis);
                    }
                    //nuevometodo();
                } else {
                    //Error Sintactico LLAVE_A
                    throw new customException(customException.ClassSinLLaveAbreCODE, t, preanalisis);
                }
            } else {
                //errorSintactico ID
                throw new customException(customException.ClassIdentificadorCODE, t, preanalisis);
            }

        } else {
            throw new customException(customException.ClassIdentificadorCODE, t, preanalisis);
        }

    }

    private void classBody() throws Exception {
        //<ClassBody> ::= id <FieldConstMethod> |
        //             <PrimitiveType> id <FieldMethod> |
        //             void <Method>|
        //             €
        //gc.iniciarClase(cs.claseActual());
        token t = new token("identificador");
        t.setNombre("ID");
        atributo sClassBody;
        if (this.preanalisis.getNombre().equals("ID")) {
            sClassBody = cs.crearHeredadofieldConstMethod(this.preanalisis.getLexema());
            parea(t);
            fieldConstMethod(sClassBody);
        } else {
            t = new token("void");
            t.setNombre("VOID");
            if (this.preanalisis.getNombre().equals("VOID")) {
                parea(t);
                sClassBody = cs.crearHeredadoMethod(cs.getVoidType());
                //gc.agregarVariablesInstancia(cs.claseActual());
                method(sClassBody);
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "PrimitiveType")) {
                    sClassBody = primitiveType();
                    t = new token("identificador");
                    t.setNombre("ID");
                    if (this.preanalisis.getNombre().equals("ID")) {
                        sClassBody = cs.crearHeredadoFieldMethod(sClassBody.getType(), this.preanalisis.getLexema());
                        parea(t);
                        fieldMethod(sClassBody);
                    }
                }
                //else LAMBDA    
            }
        }

    }

    private void fieldConstMethod(atributo heredado) throws Exception {
        //<FieldConstMethod> ::= <FormalArgs> <Block> <ClassBodyConstMethod> |
        //	         id <FieldMethod>
        token t = new token("identificador");
        t.setNombre("ID");
        atributo atrBloque = new atributo();
        if (this.preanalisis.getNombre().equals("ID")) {
            heredado = cs.crearHeredadoFieldMethod(heredado.getIdentificadorOTipo(), this.preanalisis.getLexema());
            parea(t);
            fieldMethod(heredado);
        } else {
            if (First.pertenece(this.preanalisis.getNombre(), "FormalArgs")) {
                //no voy a estar reciviendo mas variables
                //gc.agregarVariablesInstancia(cs.claseActual());
                cs.limpiarListaTemporalParametros();
                formalArgs();
                cs.controlarNombreConstructor(heredado.getIdentificadorOTipo());
                cs.controlarConstructor();
                cs.setearMetodo(cs.claseActual());
                gc.inicializoCuerpoMetodo(cs.claseActual(), cs.claseActual());
                this.esContructor = true;
                atrBloque = block();
                this.esContructor = false;
                gc.finalizarCuerpoMetodo(cs.claseActual(), cs.claseActual());
                cs.quitarConstructorEspecificacionPendiente();
                cs.controlarRetornos(cs.claseActual(), cs.claseActual(), atrBloque);
                classBodyConstMethod();

            } else {
                throw new customException(customException.fieldConstMethodCODE, preanalisis);
            }
        }
    }

    private void fieldMethod(atributo heredado) throws Exception {
        //<FieldMethod> ::= , <ListaField> |
        //		;  <ClassBody> |
        //      	<FormalArgs> <Block> <ClassBodyMethod>
        token t = new token(",");
        t.setNombre("COMA");
        atributo  atrBloque = new atributo();
        if (this.preanalisis.getNombre().equals("COMA")) {
            cs.agregarAtributoAClase(heredado.getType(), heredado.getNombreMetodo());

            parea(t);
            heredado = cs.crearHeredadoListaField(heredado.getType());
            listaField(heredado);
        } else {
            t = new token(";");
            t.setNombre("SEP");
            if (this.preanalisis.getNombre().equals("SEP")) {
                cs.agregarAtributoAClase(heredado.getType(), heredado.getNombreMetodo());
                parea(t);
                classBody();
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "FormalArgs")) {
                    //no voy a estar reciviendo mas variables
                    cs.limpiarListaTemporalParametros();
                    formalArgs();
                    cs.controlarMetodo(heredado.getNombreMetodo(), heredado.getType());
                    cs.setearMetodo(heredado.getNombreMetodo());
                    gc.inicializoCuerpoMetodo(cs.claseActual(), heredado.getNombreMetodo());
                    atrBloque = block();
                    //cs.ultimaSentenciaReturn(cs.claseActual(), heredado.getNombreMetodo(),this.ultimaInstMetodoEsReturn);
                    gc.finalizarCuerpoMetodo(cs.claseActual(), heredado.getNombreMetodo());
                    cs.quitarMetodoEspecificacionPendiente(heredado.getNombreMetodo());
                    cs.controlarRetornos(cs.claseActual(), heredado.getNombreMetodo(), atrBloque);
                    classBodyMethod();
                } else {
                    //TODO error en la declaracion de atributos
                    throw new customException(customException.DeclaracionAtributosMetodosCODE, preanalisis);
                }

            }
        }
    }

    private void listaField(atributo heredado) throws Exception {
        //<ListaField> ::= id <RestoFied>
        token t = new token("identificador");
        t.setNombre("ID");
        atributo sListaField;
        if (this.preanalisis.getNombre().equals("ID")) {
            cs.agregarAtributoAClase(heredado.getType(), this.preanalisis.getLexema());
            sListaField = cs.crearHeredadoRestoField(heredado.getType());
            parea(t);
            restoField(sListaField);
        } else {
            throw new customException(customException.DeclaracionAtributosCODE, preanalisis);
        }
    }

    private void restoField(atributo heredado) throws Exception {
        //<RestoField> ::=  , <ListaField> |
        //		; <ClassBody>

        token t = new token(",");
        t.setNombre("COMA");
        atributo sRestoField;
        if (this.preanalisis.getNombre().equals("COMA")) {
            parea(t);
            sRestoField = cs.crearHeredadoListaField(heredado.getType());
            listaField(sRestoField);
        } else {
            t = new token(";");
            t.setNombre("SEP");
            if (this.preanalisis.getNombre().equals("SEP")) {
                parea(t);
                classBody();
            } else {
                throw new customException(customException.DeclaracionListaAtributosCODE, preanalisis);
            }

        }
    }

    private void method(atributo heredado) throws Exception {
        //<Method> ::=id <FormalArgs>  <Block> <ClassBodyMethod>
        token t = new token("identificador");
        t.setNombre("ID");
        atributo atrBloque = new atributo();
        String identificador = this.preanalisis.getLexema();
        if (this.preanalisis.getNombre().equals("ID")) {
            parea(t);
            cs.limpiarListaTemporalParametros();
            formalArgs();
            cs.controlarMetodo(identificador, heredado.getType());
            cs.setearMetodo(identificador);
            gc.inicializoCuerpoMetodo(cs.claseActual(), identificador);
            atrBloque = block();
            //cs.ultimaSentenciaReturn(cs.claseActual(), identificador,this.ultimaInstMetodoEsReturn);
            gc.finalizarCuerpoMetodo(cs.claseActual(), identificador);
            cs.quitarMetodoEspecificacionPendiente(identificador);
            cs.controlarRetornos(cs.claseActual(), identificador, atrBloque);
            classBodyMethod();
        } else {
            //todo identificador
            throw new customException(customException.DeclaracionNombreMetodoCODE, preanalisis);
        }
    }

    private void classBodyConstMethod() throws Exception {
        //<ClassBodyConstMethod> ::= id <ClassBodyConstMethodResto> |
        //			<PrimitiveType> <Method> |
        //			void <Method> |
        //			€

        token t = new token("identificador");
        t.setNombre("ID");
        atributo sClassBodyConstMethod;
        if (this.preanalisis.getNombre().equals("ID")) {
            sClassBodyConstMethod = cs.crearHeredadoClassBodyConstMethodResto(this.preanalisis.getLexema());
            parea(t);
            classBodyConstMethodResto(sClassBodyConstMethod);
        } else {
            t = new token("void");
            t.setNombre("VOID");
            if (this.preanalisis.getNombre().equals("VOID")) {
                parea(t);
                sClassBodyConstMethod = cs.crearHeredadoMethod(cs.getVoidType());
                method(sClassBodyConstMethod);
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "PrimitiveType")) {
                    sClassBodyConstMethod = primitiveType();
                    sClassBodyConstMethod = cs.crearHeredadoMethod(sClassBodyConstMethod.getType());
                    method(sClassBodyConstMethod);
                }
            }
        }
        //Sino lambda
    }

    private void classBodyConstMethodResto(atributo heredado) throws Exception {
        //<ClassBodyConstMethodResto> ::= <FormalArgs> <block> <ClassBodyConstMethod> |
        //				<Method>
        atributo sClassBodyConstMethodResto;
        atributo atrBloque = new atributo();
        if (First.pertenece(this.preanalisis.getNombre(), "Method")) {
            sClassBodyConstMethodResto = cs.crearHeredadoMethod(heredado.getNombreMetodo());
            method(sClassBodyConstMethodResto);
        } else {
            if (First.pertenece(this.preanalisis.getNombre(), "FormalArgs")) {
                cs.limpiarListaTemporalParametros();
                formalArgs();
                cs.controlarNombreConstructor(heredado.getNombreMetodo());
                cs.controlarConstructor();
                cs.setearMetodo(cs.claseActual());
                gc.inicializoCuerpoMetodo(cs.claseActual(), cs.claseActual());
                this.esContructor = true;
                atrBloque = block();
                this.esContructor = false;
                gc.finalizarCuerpoMetodo(cs.claseActual(), cs.metodoActual());
                cs.quitarConstructorEspecificacionPendiente();
                //TODO ver que es cheacr valores de retorno en un constructor
                cs.controlarRetornos(cs.claseActual(), cs.claseActual(), atrBloque);
                classBodyConstMethod();
            } else {
                //TODO error en la construccion de metodos o constructores
                throw new customException(customException.comienzoParametrosMetodosConstructorCODE, preanalisis);
            }

        }

    }

    private void classBodyMethod() throws Exception {
        //<ClassBodyMethod> ::= void <Method>|
        //			<PrimitiveType> <Method>|
        //                      id <Method>|
        //                      €

        token t = new token("void");
        t.setNombre("VOID");
        atributo sClassBodyMethod;
        if (this.preanalisis.getNombre().equals("VOID")) {
            parea(t);
            sClassBodyMethod = cs.crearHeredadoMethod(cs.getVoidType());
            method(sClassBodyMethod);
        } else {
            t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                sClassBodyMethod = cs.crearHeredadoMethod(this.preanalisis.getLexema());
                parea(t);
                method(sClassBodyMethod);
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "PrimitiveType")) {
                    sClassBodyMethod = primitiveType();
                    sClassBodyMethod = cs.crearHeredadoMethod(sClassBodyMethod.getType());
                    method(sClassBodyMethod);
                }

            }
        }
    }

    private void formalArgs() throws Exception {
        //<FormalArgs>::= ( <FormalArgsListFact2>
        token t = new token("(");
        t.setNombre("PARENT_A");
        if (this.preanalisis.getNombre().equals("PARENT_A")) {
            parea(t);
            FormalArgsListFact2();
        } else {
            throw new customException(customException.comientoFormalArgsCODE, t, preanalisis);
        }
    }

    private void FormalArgsListFact2() throws Exception {
        //<FormalArgsListFact2>::= <FormalArgsList> ) | )
        token t;
        if (First.pertenece(this.preanalisis.getNombre(), "FormalArgsList")) {
            formalArgsList();
            t = new token(")");
            t.setNombre("PARENT_C");
            if (this.preanalisis.getNombre().equals("PARENT_C")) {
                parea(t);
            } else {
                throw new customException(customException.finFormalArgsCODE, t, preanalisis);
            }

        } else {
            t = new token(")");
            t.setNombre("PARENT_C");
            if (this.preanalisis.getNombre().equals("PARENT_C")) {
                parea(t);
            } else {
                //errorsintactico PARENT_C
                throw new customException(customException.finFormalArgsCODE, t, preanalisis);
            }
        }
        //else lambda
    }

    private void formalArgsListFact() throws Exception {
        //<FormalArgsListFact>::= , <FormalArgsList> |€ 
        token t = new token(",");
        t.setNombre("COMA");
        if (this.preanalisis.getNombre().equals("COMA")) {
            parea(t);
            formalArgsList();
        }
        //sino lambda

    }

    private void formalArgsList() throws Exception {
        //<FormalArgsList>::= <FormalArg> <FormalArgsListFact>
        atributo sFormalArgsList;
        sFormalArgsList = formalArg();
        cs.agregarParametrosListaTemporal(sFormalArgsList.getLexema(), sFormalArgsList.getType());
        formalArgsListFact();

    }

    private atributo formalArg() throws Exception {
        //<FormalArg>::= <Type> <identifier> 
        atributo sFormalArg;
        sFormalArg = type();
        token t = new token("identificador");
        t.setNombre("ID");
        if (this.preanalisis.getNombre().equals("ID")) {
            sFormalArg = cs.crearSintetizadoFormalArg(this.preanalisis.getLexema(), sFormalArg.getType());
            parea(t);
            return sFormalArg;
        } else {
            throw new customException(customException.identificadorFormaArgCODE, preanalisis);
        }

    }

    private atributo type() throws Exception {
        //<Type>::= <PrimitiveType> | <identifier> 
        atributo sType;
        if (First.pertenece(this.preanalisis.getNombre(), "PrimitiveType")) {
            sType = primitiveType();
        } else {
            token t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                sType = cs.crearSintetizadoType(this.preanalisis.getLexema());
                parea(t);
            } else {
                //errorsintactico
                throw new customException(customException.typeOrIDCODE, preanalisis);
            }
        }
        return sType;
    }

    private atributo primitiveType() throws Exception {
        //<PrimitiveType>::= boolean | char | int | String 
        atributo sPrimitiveType;
        sPrimitiveType = new atributo();
        token t = new token("boolean");
        t.setNombre("BOOLEAN");
        if (this.preanalisis.getNombre().equals("BOOLEAN")) {
            sPrimitiveType.setType(cs.getBooleanType());
            parea(t);
        } else {
            t = new token("char");
            t.setNombre("CHAR");
            if (this.preanalisis.getNombre().equals("CHAR")) {
                sPrimitiveType.setType(cs.getCharType());
                parea(t);
            } else {
                t = new token("int");
                t.setNombre("INT");
                if (this.preanalisis.getNombre().equals("INT")) {
                    sPrimitiveType.setType(cs.getIntType());
                    parea(t);
                } else {
                    t = new token("string");
                    t.setNombre("STRING");
                    if (this.preanalisis.getNombre().equals("STRING")) {
                        sPrimitiveType.setType(cs.getStringType());
                        parea(t);
                    } else {
                        //error no es un tipo valido.
                        throw new customException(customException.declaracionTipoCODE, preanalisis);
                    }
                }
            }
        }
        return sPrimitiveType;
    }

    private atributo block() throws Exception {
        //<Block>::= {<StatementList>}
        token t = new token("{");
        t.setNombre("LLAVE_A");
        atributo atr = new atributo();
        atr.setType(cs.getVoidType());
        if (this.preanalisis.getNombre().equals("LLAVE_A")) {
            parea(t);
            atr = statementList();
            t = new token("}");
            t.setNombre("LLAVE_C");
            if (this.preanalisis.getNombre().equals("LLAVE_C")) {
                parea(t);
            } else {
                throw new customException(customException.finBloqueCODE, t, preanalisis);
            }
        } else {
            throw new customException(customException.comientzoBloqueCODE, t, preanalisis);
        }
        return atr;

    }

    private atributo statementList() throws Exception {
        //<StatementList>::= <Statement><StatementList>  | €
        atributo atr = new atributo();
        atributo atrStatementList;
        if (First.pertenece(this.preanalisis.getNombre(), "Statement")) {
            atr = statement();
            atrStatementList = statementList();
            if(!atrStatementList.isIsEmpty()){
                atr = atrStatementList;
            }
        }
        else{
            //sino lambda
            atr.setIsEmpty(true);
            atr.setType(cs.getVoidType());
        }
        return atr;
        
    }

    private atributo statement() throws Exception {
        //<Statement>::= ;
        //<Statement>::= <ifStatement>
        //<Statement>::= <WhileStatement> 
        //<Statement>::= <ForStatement> 
        //<Statement>::= return <StatementResto>
        //<Statement>::= Block 
        //<Statement>::= <Expression>;
        //ultimaInstMetodoEsReturn = false;
        atributo atrTipoRetorno = new atributo();
        atrTipoRetorno.setType(cs.getVoidType());
        atributo sStatement;
        token t = new token(";");
        t.setNombre("SEP");
        cs.setIzquierdo(true);
        if (this.preanalisis.getNombre().equals("SEP")) {
            parea(t);
        } else {
            if (First.pertenece(this.preanalisis.getNombre(), "IfStatement")) {
                ifStatement();
            } else {
                if (First.pertenece(this.preanalisis.getNombre(), "WhileStatement")) {
                    whileStatement();
                } else {
                    if (First.pertenece(this.preanalisis.getNombre(), "ForStatement")) {
                        forStatement();
                    } else {
                        t = new token("return");
                        t.setNombre("RETURN");
                        if (this.preanalisis.getNombre().equals("RETURN")) {
                            cs.controlarSentenciaReturnEnConstructor(this.esContructor);
                            parea(t);
                            sStatement = statementResto();
                            atrTipoRetorno = sStatement;
                            //tipo de retorno de statement
                            //ultimaInstMetodoEsReturn = true;
                            cs.statementReturnControlarTipoRetorno(sStatement.getType());
                        } else {
                            if (First.pertenece(this.preanalisis.getNombre(), "Block")) {
                                block();
                            } else {
                                if (First.pertenece(this.preanalisis.getNombre(), "Expression")) {
                                    //atrTipoRetorno = 
                                    expression();
                                    t = new token(";");
                                    t.setNombre("SEP");
                                    if (this.preanalisis.getNombre().equals("SEP")) {
                                        parea(t);
                                        gc.pop();
                                    } else {
                                        //errorsintactico SEP
                                        throw new customException(customException.separadorSentenciaCODE, t, preanalisis);
                                    }
                                } else {
                                    throw new customException(customException.sentenciaCODE, preanalisis);
                                }
                                //error no es statement correcto
                            }
                        }
                    }
                }
            }
        }
        return atrTipoRetorno;
    }

    private atributo statementResto() throws Exception {
        //<RestoStmt>::= <Expression>; | ; 
        atributo sStatementResto;
        if (First.pertenece(this.preanalisis.getNombre(), "Expression")) {
            sStatementResto = expression();
            token t = new token(";");
            t.setNombre("SEP");
            if (this.preanalisis.getNombre().equals("SEP")) {
                sStatementResto = cs.crearSintetizadoStatementResto(sStatementResto.getType());
                parea(t);
            } else {
                throw new customException(customException.separadorSentenciaCODE, t, preanalisis);
            }
        } else {
            token t = new token(";");
            t.setNombre("SEP");
            if (this.preanalisis.getNombre().equals("SEP")) {
                sStatementResto = cs.crearSintetizadoStatementResto(cs.getVoidType());
                parea(t);
            } else {
                //errorsintactico SEP
                throw new customException(customException.separadorSentenciaCODE, t, preanalisis);
            }
        }
        return sStatementResto;
    }

    private void ifStatement() throws Exception {
        //<IfStatement>::=  if (Expression) <Statement> <IfStatementFact>
        atributo sIfStatement;
        token t = new token("if");
        t.setNombre("IF");
        if (this.preanalisis.getNombre().equals("IF")) {
            parea(t);
            t = new token("(");
            t.setNombre("PARENT_A");
            if (this.preanalisis.getNombre().equals("PARENT_A")) {
                parea(t);
                if (First.pertenece(this.preanalisis.getNombre(), "Expression")) {
                    sIfStatement = expression();
                    t = new token(")");
                    t.setNombre("PARENT_C");
                    if (this.preanalisis.getNombre().equals("PARENT_C")) {
                        parea(t);
                        cs.setInIf(true);
                        cs.controlarTipoExpression(cs.getBooleanType(), sIfStatement.getType());
                        cs.setInIf(false);
                        if (First.pertenece(this.preanalisis.getNombre(), "Statement")) {
                            statement();
                            ifStatementFact();
                        } else {
                            throw new customException(customException.sentenciaIFsentenciaCODE, preanalisis);
                        }
                    } else {
                        //error PARENT_C
                        throw new customException(customException.sentenciaIFParentesisCierraCODE, t, preanalisis);
                    }
                } else {
                    //error expression
                    throw new customException(customException.sentenciaIFExpresionCODE, preanalisis);
                }
            } else {
                //error parent_A
                throw new customException(customException.sentenciaIFParentesisAbreCODE, t, preanalisis);
            }
        } else {
            //errorsintactico IF
            throw new customException(t, preanalisis);
        }
    }

    private void ifStatementFact() throws Exception {
        //<IfExpresionFact>::= else <Statement> | € 
        token t = new token("else");
        t.setNombre("ELSE");
        if (this.preanalisis.getNombre().equals("ELSE")) {
            parea(t);
            statement();
        }
        //sino lambda
    }

    private void whileStatement() throws Exception {
        //<WhileStatement>::=  while ( <Expression>) <Statement> 
        atributo sWhileStatement;
        token t = new token("while");
        t.setNombre("WHILE");
        if (this.preanalisis.getNombre().equals("WHILE")) {
            parea(t);
            t = new token("(");
            t.setNombre("PARENT_A");
            if (this.preanalisis.getNombre().equals("PARENT_A")) {
                parea(t);
                sWhileStatement = expression();
                t = new token(")");
                t.setNombre("PARENT_C");
                if (this.preanalisis.getNombre().equals("PARENT_C")) {
                    parea(t);
                    cs.setInWhile(true);
                    cs.controlarTipoExpression(cs.getBooleanType(), sWhileStatement.getType());
                    cs.setInWhile(false);
                    statement();
                } else {
                    //error parent_C
                    throw new customException(customException.sentenciaWhileParentesisCierraCODE, t, preanalisis);
                }
            } else {
                //error parent_a
                throw new customException(customException.sentenciaWhileParentesisAbreCODE, t, preanalisis);
            }
        }
    }

    private void forStatement() throws Exception {
        //<ForStatement>::=  for  ( <Expression> ; <Expression> ; <Expression> ) <Statement> 
        atributo sForStatement;
        token t = new token("for");
        t.setNombre("FOR");
        if (this.preanalisis.getNombre().equals("FOR")) {
            parea(t);
            t = new token("(");
            t.setNombre("PARENT_A");
            if (this.preanalisis.getNombre().equals("PARENT_A")) {
                parea(t);
                sForStatement = expression();
                t = new token(";");
                t.setNombre("SEP");
                if (this.preanalisis.getNombre().equals("SEP")) {
                    parea(t);
                    sForStatement = expression();
                    t = new token(";");
                    t.setNombre("SEP");
                    if (this.preanalisis.getNombre().equals("SEP")) {
                        parea(t);
                        cs.setInFor(true);
                        cs.controlarTipoExpression(cs.getBooleanType(), sForStatement.getType());
                        cs.setInFor(false);
                        sForStatement = expression();
                        t = new token(")");
                        t.setNombre("PARENT_C");
                        if (this.preanalisis.getNombre().equals("PARENT_C")) {
                            parea(t);
                            statement();
                        } else {
                            //error PARENT_C
                            throw new customException(customException.sentenciaForParentesisCierraCODE, t, preanalisis);
                        }
                    } else {
                        //error SEP
                        throw new customException(customException.sentenciaForSeparadorCODE, t, preanalisis);
                    }
                } else {
                    //error SEP
                    throw new customException(customException.sentenciaForSeparadorCODE, t, preanalisis);
                }
            } else {
                //error PARENT_A
                throw new customException(customException.sentenciaForParentesisAbreCODE, t, preanalisis);
            }
        }

    }

    private atributo expression() throws Exception {
        //<Expression>::= <ConditionalOrExpression> <ExpressionFact>
        //TODO se podria preguntr por primero de conditionalOr expression.
        atributo sExpression; // 
        atributo atrCondOr = conditionalOrExpression();
        atributo atrExpFact = expressionFact();
        sExpression = cs.expression(atrCondOr, atrExpFact, this.variblesPorAlmacenar, this.gc);
        return sExpression;
    }

    private atributo expressionFact() throws Exception {
        //<ExpressionFact>::= = <Expression> | € 
        atributo sExpressionFact;
        token t = new token("=");
        t.setNombre("OP_ASIG");
        if (this.preanalisis.getNombre().equals("OP_ASIG")) {
            parea(t);
            if (First.pertenece(this.preanalisis.getNombre(), "Expression")) {
                if(!cs.isIzquierdo()){
                    this.variblesPorAlmacenar.add(posibleVariable);
                }
                cs.setIzquierdo(false);
                sExpressionFact = expression();
                sExpressionFact = cs.crearSintetizadoExpressionFact(sExpressionFact.getType(), false);
            } else {
                throw new customException(customException.expresionEnAsignacionCODE, preanalisis);
            }
        } else {
            //sino lambda
            sExpressionFact = cs.crearSintetizadoExpressionFact(true);
        }
        return sExpressionFact;
    }

    private atributo conditionalOrExpression() throws Exception {
        //<ConditionalOrExpression>::= <ConditionalAndExpression> <ConditionalOrExpressionRec> 
        atributo atrConAnd = conditionalAndExpression();
        atributo atrCondOrExpRec = conditionalOrExpressionRec();
        return cs.conditionalOrExpression(atrConAnd, atrCondOrExpRec);
    }

    private atributo conditionalOrExpressionRec() throws Exception {
        //<ConditionalOrExpressionRec>::= || <ConditionalAndExpression> <ConditionalOrExpressionRec> | € 
        atributo sConditionalOrExpressionRec;
        atributo sConditionalAndExpression;
        atributo sConditionalOrExpressionRec1;
        token t = new token("||");
        t.setNombre("OR");
        if (this.preanalisis.getNombre().equals("OR")) {
            parea(t);
            sConditionalAndExpression = conditionalAndExpression();
            sConditionalOrExpressionRec1 = conditionalOrExpressionRec();
            sConditionalOrExpressionRec = cs.conditionalOrExpressionRec(sConditionalAndExpression, sConditionalOrExpressionRec1);
        } else {
            //sino lambda
            sConditionalOrExpressionRec = cs.conditionalOrExpressionRec();
        }
        return sConditionalOrExpressionRec;
    }

    private atributo conditionalAndExpression() throws Exception {
        //<ConditionalAndExpression>::= <EqualityExpression> < ConditionalAndExpressionRec> 
        atributo atrEqExp = equalityExpression();
        atributo atrCondAndExp = conditionalAndExpressionRec();
        return cs.conditionalAndExpression(atrEqExp, atrCondAndExp);
    }

    private atributo conditionalAndExpressionRec() throws Exception {
        //<ConditionalAndExpressionRec>::= && <EqualityExpression> < ConditionalAndExpressionRec> | € 
        token t = new token("&&");
        t.setNombre("AND");
        atributo sConditionalAndExpressionRec;
        atributo sEqualityExpression;
        atributo sConditionalAndExpressionRec1;
        if (this.preanalisis.getNombre().equals("AND")) {
            this.parea(t);
            sEqualityExpression = equalityExpression();
            sConditionalAndExpressionRec1 = conditionalAndExpressionRec();
            sConditionalAndExpressionRec = cs.conditionalAndExpressionRec(sEqualityExpression, sConditionalAndExpressionRec1);
        } else {
            //sino lambda
            sConditionalAndExpressionRec = cs.conditionalAndExpressionRec();
        }
        return sConditionalAndExpressionRec;
    }

    private atributo equalityExpression() throws Exception {
        //<EqualityExpression>::=<RelationalExpression> <EqualityExpressionRec> 
        atributo atrRelExp = relationalExpression();
        atributo atrEqExpRec = equalityExpressionRec();
        return cs.equalityExpression(atrRelExp, atrEqExpRec);
    }

    private atributo equalityExpressionRec() throws Exception {
        //<EqualityExpressionRec>::= != <RelationalExpression> <EqualityExpressionRec> |  == <RelationalExpression><EqualityExpressionRec> | € 
        token t = new token("!=");
        t.setNombre("DISTINTO");
        atributo sEqualityExpressionRec;
        atributo sRelationalExpression;
        atributo sEqualityExpressionRec1;
        if (this.preanalisis.getNombre().equals("DISTINTO")) {
            parea(t);
            sRelationalExpression = relationalExpression();
            sEqualityExpressionRec1 = equalityExpressionRec();
            sEqualityExpressionRec = cs.equalityExpressionRec(sRelationalExpression, sEqualityExpressionRec1);
            gc.ne();
        } else {
            t = new token("==");
            t.setNombre("IGUAL");
            if (this.preanalisis.getNombre().equals("IGUAL")) {
                parea(t);
                sRelationalExpression = relationalExpression();
                sEqualityExpressionRec1 = equalityExpressionRec();
                sEqualityExpressionRec = cs.equalityExpressionRec(sRelationalExpression, sEqualityExpressionRec1);
                gc.eq();
            } else {
                //sino lambda
                sEqualityExpressionRec = cs.equalityExpressionRec();
            }

        }
        return sEqualityExpressionRec;
    }

    private atributo relationalExpression() throws Exception {
        //<RelationalExpression>::= <AdditiveExpression><RelationalExpressionFact >  
        atributo atrAddExp = additiveExpression();
        atributo atrRelExpFact = relationalExpressionFact();
        return cs.relationalExpression(atrAddExp, atrRelExpFact);
    }

    private atributo relationalExpressionFact() throws Exception {
        //<RelationalExpressionFact>::= >= <AdditiveExpression> | <= <AdditiveExpression> | > <AdditiveExpression> | < <AdditiveExpression> 
        token t = new token(">=");
        t.setNombre("MAYOR_IGUAL");
        atributo sRelationalExpressionFact;
        atributo sAdditiveExpression;
        if (this.preanalisis.getNombre().equals("MAYOR_IGUAL")) {
            parea(t);
            sAdditiveExpression = additiveExpression();
            sRelationalExpressionFact = cs.additiveExpression(sAdditiveExpression);
            gc.ge();
        } else {
            t = new token("<=");
            t.setNombre("MENOR_IGUAL");
            if (this.preanalisis.getNombre().equals("MENOR_IGUAL")) {
                parea(t);
                sAdditiveExpression = additiveExpression();
                sRelationalExpressionFact = cs.additiveExpression(sAdditiveExpression);
                gc.le();
            } else {
                t = new token(">");
                t.setNombre("MAYOR");
                if (this.preanalisis.getNombre().equals("MAYOR")) {
                    parea(t);
                    sAdditiveExpression = additiveExpression();
                    sRelationalExpressionFact = cs.additiveExpression(sAdditiveExpression);
                    gc.gt();
                } else {
                    t = new token("<");
                    t.setNombre("MENOR");
                    if (this.preanalisis.getNombre().equals("MENOR")) {
                        parea(t);
                        sAdditiveExpression = additiveExpression();
                        sRelationalExpressionFact = cs.additiveExpression(sAdditiveExpression);
                        gc.lt();
                    } else {
                        sRelationalExpressionFact = new atributo();
                        sRelationalExpressionFact.setIsEmpty(true);
                    }
                }

            }
        }
        return sRelationalExpressionFact;
    }

    private atributo additiveExpression() throws Exception {
        //<AdditiveExpression>::= <MultiplicativeExpression> <AdditiveExpressionRec>  
        atributo atrMultExp = multiplicativeExpression();
        atributo atrAddExpRec = additiveExpressionRec();
        return cs.additiveExpression(atrMultExp, atrAddExpRec);
    }

    private atributo additiveExpressionRec() throws Exception {
        //<AdditiveExpressionRec>::=  -   <MultiplicativeExpression><AdditiveExpressionRec>  |  + <MultiplicativeExpression><AdditiveExpressionRec> | € 
        atributo sAdditiveExpressionRec;
        atributo sMultiplicativeExpression;
        atributo sAdditiveExpressionRec1;
        token t = new token("-");
        t.setNombre("OP_RESTA");
        if (this.preanalisis.getNombre().equals("OP_RESTA")) {
            parea(t);
            sMultiplicativeExpression = multiplicativeExpression();
            sAdditiveExpressionRec1 = additiveExpressionRec();
            sAdditiveExpressionRec = cs.additiveExpressionRec(sMultiplicativeExpression, sAdditiveExpressionRec1);
            gc.restar();
        } else {
            t = new token("+");
            t.setNombre("OP_SUMA");
            if (this.preanalisis.getNombre().equals("OP_SUMA")) {
                parea(t);
                sMultiplicativeExpression = multiplicativeExpression();
                sAdditiveExpressionRec1 = additiveExpressionRec();
                sAdditiveExpressionRec = cs.additiveExpressionRec(sMultiplicativeExpression, sAdditiveExpressionRec1);
                gc.sumar();
            } else {
                //sino lambda
                sAdditiveExpressionRec = cs.additiveExpressionRec();
            }
        }
        return sAdditiveExpressionRec;
    }

    private atributo multiplicativeExpression() throws Exception {
        //<MultiplicativeExpression>::=<UnaryExpression> <MultiplicativeExpressionRec> 
        atributo atrUnary = unaryExpression();
        atributo atrMulExpRec = multiplicativeExpressionRec();
        return cs.multiplicativeExpression(atrUnary, atrMulExpRec);
    }

    private atributo multiplicativeExpressionRec() throws Exception {
        //<MultiplicativeExpressionRec>::=  % <UnaryExpression> <MultiplicativeExpressionRec> | /  <UnaryExpression><MultiplicativeExpressionRec> | *  <UnaryExpression><MultiplicativeExpressionRec> | € 
        token t = new token("%");
        t.setNombre("OP_MOD");
        atributo sMultiplicativeExpressionRec;
        atributo sUnaryExpression;
        atributo sMultiplicativeExpressionRec1;
        if (this.preanalisis.getNombre().equals("OP_MOD")) {
            parea(t);
            sUnaryExpression = unaryExpression();
            sMultiplicativeExpressionRec1 = multiplicativeExpressionRec();
            sMultiplicativeExpressionRec = cs.multiplicativeExpressionRec(sUnaryExpression, sMultiplicativeExpressionRec1);
            gc.modulo();
        } else {
            t = new token("/");
            t.setNombre("OP_DIV");
            if (this.preanalisis.getNombre().equals("OP_DIV")) {
                parea(t);
                sUnaryExpression = unaryExpression();
                sMultiplicativeExpressionRec1 = multiplicativeExpressionRec();
                sMultiplicativeExpressionRec = cs.multiplicativeExpressionRec(sUnaryExpression, sMultiplicativeExpressionRec1);
                gc.dividir();
            } else {
                t = new token("*");
                t.setNombre("OP_MULT");
                if (this.preanalisis.getNombre().equals("OP_MULT")) {
                    parea(t);
                    sUnaryExpression = unaryExpression();
                    sMultiplicativeExpressionRec1 = multiplicativeExpressionRec();
                    sMultiplicativeExpressionRec = cs.multiplicativeExpressionRec(sUnaryExpression, sMultiplicativeExpressionRec1);
                    gc.multiplicar();
                } else {
                    //lambda
                    sMultiplicativeExpressionRec = cs.multiplicativeExpressionRec();
                }
            }
        }
        return sMultiplicativeExpressionRec;
    }

    private atributo unaryExpression() throws Exception {
        //<UnaryExpression>::=  + <UnaryExpression>| 
        //                      - <UnaryExpression>| 
        //                       ! <UnaryExpression>| 
        //                       <Primary >
        token t = new token("+");
        t.setNombre("OP_SUMA");
        atributo sintetizado;
        atributo unaryRec;
        atributo sPrimary;
        if (this.preanalisis.getNombre().equals("OP_SUMA")) {
            parea(t);
            unaryRec = unaryExpression();
            sintetizado = cs.unaryExpression(unaryRec);
        } else {
            t = new token("-");
            t.setNombre("OP_RESTA");
            if (this.preanalisis.getNombre().equals("OP_RESTA")) {
                parea(t);               
                unaryRec = unaryExpression();                
                gc.neg();
                sintetizado = cs.unaryExpression(unaryRec);
            } else {
                t = new token("!");
                t.setNombre("NOT");
                if (this.preanalisis.getNombre().equals("NOT")) {
                    parea(t);
                    unaryRec = unaryExpression();
                    gc.not();
                    sintetizado = cs.unaryExpression(unaryRec);
                } else {
                    // tener cuidado aca 
                    sPrimary = primary();
                    sintetizado = cs.unaryExpressionPrimary(sPrimary);
                }
            }
        }
        return sintetizado;
    }

    private atributo primary() throws Exception {
        // <primary>::= identifier <restoid> |
        //              this <restothis>     |
        //              Literal <primaryRec> |
        //              ( <expresión>) <primaryRec> |
        //              new identifier <actualArgs> <primaryRec>  |
        //              super . identifier <actualArgs> <primaryRec>
        atributo sintetizado;
        token t = new token("identificador");
        t.setNombre("ID");//podemos encontrar system
        atributo atr;
        if (this.preanalisis.getNombre().equals("ID")) {
            atr = new atributo();
            atr.setLexema(this.preanalisis.getLexema());
            parea(t);
            sintetizado = restoId(atr);
        } else {
            t = new token("(");
            t.setNombre("PARENT_A");
            if (this.preanalisis.getNombre().equals("PARENT_A")) {
                parea(t);
                atr = expression();
                t = new token(")");
                t.setNombre("PARENT_C");
                if (this.preanalisis.getNombre().equals("PARENT_C")) {
                    atr = cs.primaryExpressionToPrimaryRec(atr);
                    parea(t);
                    sintetizado = primaryRec(atr);
                } else {
                    //error PARENT_C
                    throw new customException(customException.expresionParentisacionCODE, t, preanalisis);
                }
            } else {
                t = new token("new");
                t.setNombre("NEW");
                if (this.preanalisis.getNombre().equals("NEW")) {
                    parea(t);
                    t = new token("identificador");
                    t.setNombre("ID");
                    String nombreClase = this.preanalisis.getLexema();
                    if (this.preanalisis.getNombre().equals("ID")) {
                        atr = cs.primaryNewIdToActualArgs(this.preanalisis.getLexema());
                        parea(t);
                        atr = actualArgs(atr);
                        gc.invocarConstructorClase(nombreClase);
                        atr = cs.primaryActualArgsToPrimaryRec(atr);
                        sintetizado = primaryRec(atr);
                    } else {
                        //error ID
                        throw new customException(customException.expresionNewNecesitaIDCODE, preanalisis);
                    }
                } else {
                    //super  . identifier  <ActualArgs><PrimaryRec> | 
                    t = new token("super");
                    t.setNombre("SUPER");
                    if (this.preanalisis.getNombre().equals("SUPER")) {
                        parea(t);
                        t = new token(".");
                        t.setNombre("PUNTO");
                        if (this.preanalisis.getNombre().equals("PUNTO")) {
                            parea(t);
                            t = new token("identificador");
                            t.setNombre("ID");
                            if (this.preanalisis.getNombre().equals("ID")) {
                                atr = cs.primarySuperIdToActualArgs(this.preanalisis.getLexema());
                                parea(t);
                                atr = actualArgs(atr);
                                atr = cs.primaryActualArgsToPrimaryRec(atr);
                                sintetizado = primaryRec(atr);
                            } else {
                                //error ID
                                throw new customException(customException.expresionSuperNecesitaIDCODE, preanalisis);
                            }
                        } else {
                            //error PUNTO
                            throw new customException(customException.expresionSuperNecesitaPuntoCODE, t, preanalisis);
                        }
                    } else {
                        if (First.pertenece(this.preanalisis.getNombre(), "Literal")) {
                            atr = literal();
                            atr = cs.primaryLiteralToPrimaryRec(atr);
                            sintetizado = primaryRec(atr);
                        } else {
                            t = new token("this");
                            t.setNombre("THIS");
                            if (this.preanalisis.getNombre().equals("THIS")) {
                                parea(t);
                                atributo sRestoThis;
                                sRestoThis = restoThis();
                                sintetizado = new atributo();
                                sintetizado.setType(sRestoThis.getType());
                                sintetizado.setAsignable(sRestoThis.isAsignable());
                            } else {
                                throw new customException(customException.expresionMalFormadaCODE, preanalisis);
                            }
                        }
                    }
                }
            }
        }
        return sintetizado;
    }

    private atributo restoThis() throws Exception {
        //<Restothis> ::= . identifier <RestothisFact> | Є
        atributo sintetizado = new atributo();
        token t = new token(".");
        t.setNombre("PUNTO");
        if (this.preanalisis.getNombre().equals("PUNTO")) {
            parea(t);
            t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                atributo atr;
                atr = cs.restoThisIDTorestoThisFact(this.preanalisis.getLexema());
                parea(t);
                sintetizado = restoThisFact(atr);
            }
        } else {
            //lambda
            sintetizado = cs.restoThisEmpty();
        }
        return sintetizado;
    }

    private atributo restoThisFact(atributo atr) throws Exception {
        //<restoThisFact> ::=  <PrimaryRec> | <ActualArgs> <PrimaryRec>
        atributo sintetizado;
        if (First.pertenece(this.preanalisis.getNombre(), "ActualArgs")) {
            atr = cs.restoThisFactActualArgsToPrimary(atr);
            atr = actualArgs(atr);
            sintetizado = primaryRec(atr);
        } else {
            //TODO preguntar si pertenece a FIRST  primaryRec
            String identificadorVariable = atr.getLexema();
            atr = cs.restoThisFactPrimary(atr);
            atr = primaryRec(atr);
            sintetizado = cs.controlarAtributosPrivados(atr, identificadorVariable);
        }
        return atr;
    }

    private atributo restoId(atributo atr) throws Exception {
        //<RestoId> ::=  <PrimaryRec> | <ActualArgs> <PrimaryRec>
        atributo sintetizado;
        String identificador = atr.getLexema();
        if (First.pertenece(this.preanalisis.getNombre(), "ActualArgs")) {
            atr = cs.restoIdBeforeActualArgs(atr);
            atr = actualArgs(atr);
            gc.invocarMetodo(cs.claseActual(), identificador);
            sintetizado = primaryRec(atr);
        } else {
            atr = cs.primaryRecIsAvar(atr);
            //esta variable puede ser un parametro o una variable de instancia
            //Andres
            //aca tengo setead tipo y lexema
            atr.setIsAvar(true);
            sintetizado = primaryRec(atr);
            if(sintetizado.isIsEmpty()){
                if(cs.isIzquierdo()){
                    //this.identificadorVariable = identificador;
                    this.variblesPorAlmacenar.add(identificador);
                    
                }
                else{
                    posibleVariable = identificador;
                    gc.ponerVariableEnPila(identificador);
                }
                
            }

        }
        return sintetizado;
    }

    private atributo primaryRec(atributo atr) throws Exception {
        //<PrimaryRec>::= .  identifier <ActualArgs> <PrimaryRec>| € 
        atributo sintetizado;
        token t = new token(".");
        t.setNombre("PUNTO");
        if (this.preanalisis.getNombre().equals("PUNTO")) {
            cs.primaryRecIsLiteral(atr);
            parea(t);
            t = new token("identificador");
            t.setNombre("ID");
            if (this.preanalisis.getNombre().equals("ID")) {
                //identificador es el nombre del metodod
                String identificador = this.preanalisis.getLexema();
                //tipoObjeto es la clase a la que se le esta invocando el metodo
                String tipoObjeto = atr.getType();
                atr = cs.primaryRecIdToActualArgs(atr, identificador);
                parea(t);
                //reservo Espacio para el valor de retorno
                //e intercambio el this para tener el objeto this en el tope
                atr = actualArgs(atr);
                gc.invocarMetodo(tipoObjeto, identificador);
                atr = cs.primaryRecActualArgsToPrimaryRec(atr);
                sintetizado = primaryRec(atr);
                if(sintetizado.isIsEmpty()){
                   sintetizado = atr; 
                }
                sintetizado.setAsignable(false);
            } else {
                throw new customException(customException.identificadorNombreMetodoCODE, preanalisis);
            }
        } else {
            //Sino Lambda
            sintetizado = cs.primaryRecEmpty(atr);
        }
        return sintetizado;
    }

    /*diego
    private int prueba() {
        boolean a;
        int i=3;
        if(1=2) { return i;}
        if (i<2) {
            a = true;
            if(i >4){
                return 1;
            }
            else{
            
            }
            return 2;
            
        } else {
            a = false;
            return 1;
        }
    }*/

    private atributo literal() throws Exception {
        //<Literal> → null | true | false | intLiteral | charLiteral | stringLiteral 
        token t = new token("null");
        t.setNombre("NULL");
        atributo sintetizado;
        sintetizado = new atributo();
        if (this.preanalisis.getNombre().equals("NULL")) {
            sintetizado.setType(cs.getNullType());
            parea(t);
        } else {
            t = new token("true");
            t.setNombre("TRUE");
            if (this.preanalisis.getNombre().equals("TRUE")) {
                gc.push(1); 
                sintetizado.setType(cs.getBooleanType());
                parea(t);
            } else {
                t = new token("false");
                t.setNombre("FALSE");
                if (this.preanalisis.getNombre().equals("FALSE")) {
                    gc.push(0); 
                    sintetizado.setType(cs.getBooleanType());
                    parea(t);
                } else {
                    t = new token("num");
                    t.setNombre("NUM");
                    if (this.preanalisis.getNombre().equals("NUM")) {
                        //TODO chequear maxint
                        cs.controlarNumero(this.preanalisis.getLexema());
                        //
                        //gc.ponerLiteralEnPila(this.preanalisis.getLexema());
                        sintetizado.setType(cs.getIntType());
                        parea(t);
                    } else {
                        t = new token("charLiteral");
                        t.setNombre("CHARLITERAL");
                        if (this.preanalisis.getNombre().equals("CHARLITERAL")) {
                            gc.pushLabel(this.preanalisis.getLexema());
                            sintetizado.setType(cs.getCharType());
                            parea(t);
                        } else {
                            t = new token("stringLiteral");
                            t.setNombre("STRINGLITERAL");
                            if (this.preanalisis.getNombre().equals("STRINGLITERAL")) {
                                //chequear longitud string
                                gc.traducirString(this.preanalisis.getLexema());
                                sintetizado.setType(cs.getStringType());
                                parea(t);
                            } else {
                                //error no es un literal    
                                throw new customException(customException.literalCODE, preanalisis);
                            }
                        }
                    }
                }
            }
        }
        return sintetizado;
    }

    private atributo actualArgs(atributo atr) throws Exception {
        //<ActualArgs>::= ( <ActualArgsResto>
        token t = new token("(");
        t.setNombre("PARENT_A");
        atributo aux = atr;
        if (this.preanalisis.getNombre().equals("PARENT_A")) {
            parea(t);
            atr = cs.actualArgsBeforeActualArgsResto(atr);
            actualArgsResto(atr);
            atr = cs.actualArgsTipoRetornoMetodo(aux);
        } else {
            //ErrorSintactico
            throw new customException(customException.paramActParentesisAbreCODE, t, preanalisis);
        }
        atr.setIsNew(aux.isIsNew());
        atr.setAsignable(false);
        return atr;
    }

    private void actualArgsResto(atributo atr) throws Exception {
        //ActualArgsResto→  ExprList ) | )
        token t;
        atributo aux = atr;
        if (First.pertenece(this.preanalisis.getNombre(), "ExprList")) {
            atr = cs.actualArgsRestoBeforeExprList(atr);
            atr = exprList(atr);
            t = new token(")");
            t.setNombre("PARENT_C");
            if (this.preanalisis.getNombre().equals("PARENT_C")) {
                cs.controlarCantidadParametrosActuales(aux, atr);
                //Para no perder la referncia al CIR al realizar un LOADREF
                //gc.duplicarTope();
                parea(t);
            } else {
                //ErrorSintactico
                throw new customException(customException.paramActParentesisCierraCODE, t, preanalisis);
            }
        } else {
            t = new token(")");
            t.setNombre("PARENT_C");
            if (this.preanalisis.getNombre().equals("PARENT_C")) {
                cs.controlarCantidadParametrosActuales(atr, atr);
                parea(t);
            } else {
                //ErrorSintactico
                throw new customException(customException.paramActParentesisCierraCODE, t, preanalisis);
            }
        }
        //else LAMBDA
    }

    private atributo exprList(atributo atr) throws Exception {
        //<actualArg>::= <expression>
        //ExprList→  Expression ExprListFact
        //Expression evalua el resultado generando el codigo dentro de la expression
        atributo aExpr = expression();
        //intercambio el objeto this al tope de la pila
        //gc.intercambiarTope();
        atr = cs.exprListExpToExprListFact(atr, aExpr);
        atr = exprListFact(atr);
        return atr;
    }

    private atributo exprListFact(atributo atr) throws Exception {
        //<ActualArgFact>::= , <ActualArgList> | €
        token t = new token(",");
        t.setNombre("COMA");
        if (this.preanalisis.getNombre().equals("COMA")) {
            parea(t);
            atr = cs.exprListFactToExprList(atr);
            atr = exprList(atr);
            atr = cs.asignarPosParametro(atr);
        } else {
            //Sino Lambda
            atr = cs.asignarPosParametro(atr);
        }
        return atr;
    }
}
